﻿using System.Collections;
using System.Reflection;
namespace System.Web
{
    internal partial class NetHttpApplicationFactory
    {
        private int _numFreeSpecialInstances;
        private Stack _freeSpecialInstances = new Stack();
        private MethodInfo _onStartMethod;
        private int _onStartParamCount;
        private MethodInfo _onEndMethod;
        private int _onEndParamCount;
        private MethodInfo _sessionOnEndMethod;
        private int _sessionOnEndParamCount;

        private void EnsureAppStartCalled(NetHttpContext context)
        {
            if (!_appOnStartCalled)
                lock (this)
                    if (!_appOnStartCalled)
                    {
                        FireApplicationOnStart(context);
                        _appOnStartCalled = true;
                    }
        }

        internal void EnsureAppStartCalledForIntegratedMode(NetHttpContext context, NetHttpApplication application)
        {
            if (!_appOnStartCalled)
            {
                Exception innerException = null;
                lock (this)
                {
                    if (!_appOnStartCalled)
                        if (_onStartMethod != null)
                            application.ProcessSpecialRequest(context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null);
                    _appOnStartCalled = true;
                    innerException = context.Error;
                }
                if (innerException != null)
                    throw new HttpException(innerException.Message, innerException);
            }
        }

        private void FireApplicationOnEnd()
        {
            if (_onEndMethod != null)
            {
                var application = GetSpecialApplicationInstance(null);
                application.ProcessSpecialRequest(null, _onEndMethod, _onEndParamCount, this, EventArgs.Empty, null);
                RecycleSpecialApplicationInstance(application);
            }
        }

        private void FireApplicationOnError(Exception error)
        {
            var application = GetSpecialApplicationInstance(null);
            application.RaiseErrorWithoutContext(error);
            RecycleSpecialApplicationInstance(application);
        }

        private void FireApplicationOnStart(NetHttpContext context)
        {
            if (_onStartMethod != null)
            {
                var application = GetSpecialApplicationInstance(null);
                application.ProcessSpecialRequest(context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null);
                RecycleSpecialApplicationInstance(application);
            }
        }

        private void FireSessionOnEnd(HttpSessionStateBase session, object eventSource, EventArgs eventArgs)
        {
            if (_sessionOnEndMethod != null)
            {
                var application = GetSpecialApplicationInstance(null);
                application.ProcessSpecialRequest(null, _sessionOnEndMethod, _sessionOnEndParamCount, eventSource, eventArgs, session);
                RecycleSpecialApplicationInstance(application);
            }
        }

        private NetHttpApplication GetSpecialApplicationInstance(NetHttpContext context)
        {
            NetHttpApplication application = null;
            lock (_freeSpecialInstances)
                if (_numFreeSpecialInstances > 0)
                {
                    application = (NetHttpApplication)_freeSpecialInstances.Pop();
                    _numFreeSpecialInstances--;
                }
            if (application == null)
            {
                application = (NetHttpApplication)NetHttpRuntime.CreateNonPublicInstance(_applicationType, null);
                application.InitSpecial(context, _state, this, _eventHandlerMethods);
            }
            return application;
        }

        private void RecycleSpecialApplicationInstance(NetHttpApplication application)
        {
            if (_numFreeSpecialInstances < 20)
                lock (_freeSpecialInstances)
                {
                    _freeSpecialInstances.Push(application);
                    _numFreeSpecialInstances++;
                }
        }
    }
}
